package com.ssamkj.neuro;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Arrays;

import org.junit.Ignore;
import org.junit.Test;
import org.neuroph.core.Connection;
import org.neuroph.core.Layer;
import org.neuroph.core.NeuralNetwork;
import org.neuroph.core.Neuron;
import org.neuroph.core.data.BufferedDataSet;
import org.neuroph.core.data.DataSet;
import org.neuroph.core.data.DataSetRow;
import org.neuroph.core.events.LearningEvent;
import org.neuroph.core.events.LearningEventListener;
import org.neuroph.nnet.Hopfield;
import org.neuroph.nnet.MultiLayerPerceptron;
import org.neuroph.nnet.Perceptron;
import org.neuroph.nnet.learning.BackPropagation;
import org.neuroph.util.random.DistortRandomizer;
import org.neuroph.util.random.NguyenWidrowRandomizer;

public class NeuroTest1 {

	@Ignore
	@Test
	public void hopFieldTest() {
		System.out.println("hopFieldTest ==================================");
		// create training set (H and T letter in 3x3 grid)
		DataSet trainingSet = new DataSet(9);
		trainingSet.addRow(new DataSetRow(new double[] { 100, 0, 1, 1, 1, 1, 1,
				0, 1 })); // H letter

		trainingSet.addRow(new DataSetRow(new double[] { 1, 1, 1, 0, 1, 0, 0,
				1, 0 })); // T letter

		// create hopfield network
		Hopfield myHopfield = new Hopfield(9);
		// learn the training set
		myHopfield.learn(trainingSet);

		// test hopfield network
		System.out.println("Testing network");

		// add one more 'incomplete' H pattern for testing - it will be
		// recognized as H
		trainingSet.addRow(new DataSetRow(new double[] { 1, 0, 0, 1, 0, 1, 1,
				0, 1 }));

		// print network output for the each element from the specified training
		// set.
		for (DataSetRow trainingSetRow : trainingSet.getRows()) {
			myHopfield.setInput(trainingSetRow.getInput());
			myHopfield.calculate();
			myHopfield.calculate();
			double[] networkOutput = myHopfield.getOutput();

			System.out.print("Input: "
					+ Arrays.toString(trainingSetRow.getInput()));
			System.out.println(" Output: " + Arrays.toString(networkOutput));
		}

	}

	/**
	 * Runs this sample
	 */
	@Test
	public void perceptronTest() {
		System.out
				.println("perceptronTest====================================");
		// create training set (logical AND function)
		DataSet trainingSet = new DataSet(2, 1);
		trainingSet.addRow(new DataSetRow(new double[] { 0, 0 }, new double[] {
				0  }));
		trainingSet.addRow(new DataSetRow(new double[] { 0, 1 }, new double[] {
				0 }));
		trainingSet.addRow(new DataSetRow(new double[] { 1, 0 }, new double[] {
				0 }));
		trainingSet.addRow(new DataSetRow(new double[] { 1, 1 }, new double[] {
				1 }));

		// create perceptron neural network
		NeuralNetwork myPerceptron = new Perceptron(2, 1);
		// learn the training set
		myPerceptron.learn(trainingSet);
		// test perceptron
		System.out.println("Testing trained perceptron");
		testNeuralNetwork(myPerceptron, trainingSet);
		
		// save trained perceptron
		// myPerceptron.save("mySamplePerceptron.nnet");
		// load saved neural network
		// NeuralNetwork loadedPerceptron =
		// NeuralNetwork.load("mySamplePerceptron.nnet");
		// // test loaded neural network
		// System.out.println("Testing loaded perceptron");
		// testNeuralNetwork(loadedPerceptron, trainingSet);
	}

	/**
	 * Prints network output for the each element from the specified training
	 * set.
	 * 
	 * @param neuralNet
	 *            neural network
	 * @param testSet
	 *            data set used for testing
	 */
	private void testNeuralNetwork(NeuralNetwork neuralNet, DataSet testSet) {

		for (DataSetRow trainingElement : testSet.getRows()) {
			neuralNet.setInput(trainingElement.getInput());
			neuralNet.calculate();
			double[] networkOutput = neuralNet.getOutput();

			System.out.print("Input: "
					+ Arrays.toString(trainingElement.getInput()));
			System.out.println(" Output: " + Arrays.toString(networkOutput));
		}
		
	}

	/**
	 * Runs this sample
	 */
	@Ignore
	@Test
	public void MultiLayerPerceptronTest() {
		System.out
				.println("MultiLayerPerceptronTest=====================================");
		MultiLayerPerceptron neuralNet = new MultiLayerPerceptron(2, 3, 1);
		// neuralNet.randomizeWeights(new WeightsRandomizer());
		// neuralNet.randomizeWeights(new RangeRandomizer(0.1, 0.9));
		// neuralNet.randomizeWeights(new GaussianRandomizer(0.4, 0.3));
		neuralNet.randomizeWeights(new NguyenWidrowRandomizer(0.3, 0.7));
		printWeights(neuralNet);

		neuralNet.randomizeWeights(new DistortRandomizer(0.5));
		printWeights(neuralNet);
	}

	private void printWeights(NeuralNetwork neuralNet) {
		for (Layer layer : neuralNet.getLayers()) {
			for (Neuron neuron : layer.getNeurons()) {
				for (Connection connection : neuron.getInputConnections()) {
					System.out.print(connection.getWeight().value + " ");
				}
				System.out.println();
			}
		}
	}

	@Ignore
	@Test
	public void BufferedDataSetSample() throws FileNotFoundException {
		System.out
				.println("BufferedDataSetSample==========================================");
		BufferedDataSetSample b = new BufferedDataSetSample();

	}

}

class BufferedDataSetSample implements LearningEventListener {

	public BufferedDataSetSample() throws FileNotFoundException {
		run();
	}

	public void run() throws FileNotFoundException {
		String inputFileName = BufferedDataSetSample.class.getResource(
				"data/iris_data_normalised.txt").getFile();
		// create MultiLayerPerceptron neural network
		MultiLayerPerceptron neuralNet = new MultiLayerPerceptron(4, 16, 3);
		BufferedDataSet irisDataSet = new BufferedDataSet(new File(
				inputFileName), 4, 3, ",");
		neuralNet.getLearningRule().addListener(this);
		neuralNet.learn(irisDataSet);

		// neuralNet.getLearningRule().setMaxError(0.001);
	}

	public void handleLearningEvent(LearningEvent event) {
		BackPropagation bp = (BackPropagation) event.getSource();
		System.out.println(bp.getCurrentIteration() + ". iteration : "
				+ bp.getTotalNetworkError());
	}

}